package com.rengwuxian.coursecoroutines._2_structured_concurrency

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext

/**
 * 父子协程，以及协程间的并行和等待
 * Job、CoroutineScope、父子协程、并行协程
 *
 * 看Job的角度即可。结构化并发。
 * 在协程结束的时候、取消以及遇到异常的时候，对它的子协程和父协程有怎样的自动化影响。
 * 1.父子协程的关系是怎么确立的？
 * 最简单的方式：
 *   val scope = CoroutineScope(EmptyCoroutineContext)
 *   var innerJob: Job? = null
 *   val initJob = scope.launch {
 *     innerJob = launch { delay(100) }
 *     launch {  }
 *   }
 * val children = initJob.children
 * println("children count: ${children.count()}")
 * println("innerJob === children.first(): ${innerJob === children.first()}")
 * 注意：父子关系是动态的，一旦协程运行结束，这种关系就解绑了。
 * 父子关系的确立：看下启动协程的CoroutineScope的Job是谁，谁就是这个启动协程的父协程。而非结构。
 *   var innerJob: Job? = null
 *   val job = scope.launch {
 *     launch(Job()) {
 *       delay(100)
 *     }
 *     val customJob = Job()
 *     innerJob = launch(customJob) {
 *       delay(100)
 *     }
 *   }
 * 看Job是谁即可。
 *
 * 协程的结构化结束：父协程会等待所有子协程结束，才会结束它自己。
 *
 * 所有协程之间都是并行的，包括父子协程，如果都运行在主线程上，只是会在运行上受到实际限制，而流程上是属于并行关系的。
 *
 *   val job = scope.launch {
 *     launch() {
 *       delay(100)
 *     }
 *     // val customJob = Job()
 *     // innerJob = launch(customJob) {
 *     // delay(100)
 *     }
 *   }
 *   val startTime = System.currentTimeMillis()
 *   job.join()
 *   val duration = System.currentTimeMillis() - startTime
 *   println("duration: $duration")
 *
 *   结果大概为100ms，而如果解开注释代码，则会时间很少，协程立刻就结束了。
 *   协程的结构化结束，是最简单的，相较于结构化取消，而结构化异常管理。
 */
@OptIn(ExperimentalCoroutinesApi::class)
fun main() = runBlocking<Unit> {
  val scope = CoroutineScope(EmptyCoroutineContext)
  val initJob = scope.launch {
    launch {  }
    launch {  }
  }
  scope.launch {
    initJob.join()
    // ???
  }
  var innerJob: Job? = null
  val job = scope.launch {
    launch(Job()) {
      delay(100)
    }
//    val customJob = Job()
//    innerJob = launch(customJob) {
//      delay(100)
//    }
  }
  val startTime = System.currentTimeMillis()
  job.join()
  val duration = System.currentTimeMillis() - startTime
  println("duration: $duration")
//  val children = job.children
//  println("children count: ${children.count()}")
//  println("innerJob === children.first(): ${innerJob === children.first()}")
//  println("innerJob.parent === job: ${innerJob?.parent === job}")
}